#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include<time.h>
 
#define phi_num 5
#define think_time 2
#define eat_time 1
#define left (phi_id)%phi_num
#define right (phi_id+1)%phi_num

//加锁方式:阻塞加锁，非阻塞加锁，非阻塞加锁+奇偶哲学家取筷子顺序不同
enum lock_way{block_lock,unblock_lock,odd_even} ;
enum lock_way LOCKWAY=odd_even;

pthread_mutex_t mutex[phi_num];
int eat_times[phi_num];

//哲学家思考
void Do_think(int phi_id){
	printf("philosopher %d is thinking now !\n",phi_id);
	sleep(think_time);
}

//哲学家吃饭
void Do_eat(int phi_id){
	printf("philosopher %d is eating now !\n",phi_id);
	sleep(eat_time);
}

//哲学家取筷子
int Do_take_forks(int phi_id){
    //阻塞加锁方式
    if(LOCKWAY==block_lock){
        pthread_mutex_lock(&mutex[left]);
        printf("block_lock philosopher %d fetched left chopstick %d !\n",phi_id,left);
        pthread_mutex_lock(&mutex[right]);
        printf("block_lock philosopher %d fetched right chopstick %d !\n",phi_id,right);
        return 0;

    //非阻塞加锁方式
    }else if(LOCKWAY==unblock_lock){
        //先取左边筷子
        if(pthread_mutex_trylock(&mutex[left])!=0){
            return -1;
        }
        printf("unblock_lock philosopher %d fetched left chopstick %d !\n",phi_id,left);
        //后取右边筷子,如果不成功，放下左边筷子
        if(pthread_mutex_trylock(&mutex[right])!=0){
            pthread_mutex_unlock(&mutex[left]);
            printf("unblock_lock philosopher %d fetch right chopstick %d failed and released left chopstick %d! \n",phi_id,right,left);
            return -1;
        }
        printf("unblock_lock philosopher %d fetched right chopstick %d !\n",phi_id,right);
        return 0;
    
    //非阻塞加锁+奇偶哲学家取筷子顺序不同
    }else if(LOCKWAY==odd_even){
        //偶数哲学家
        if(phi_id%2==0){
            if(pthread_mutex_trylock(&mutex[left])!=0){
                return -1;
            }
            printf("odd_even philosopher %d fetched left chopstick %d !\n",phi_id,left);
            if(pthread_mutex_trylock(&mutex[right])!=0){
                pthread_mutex_unlock(&mutex[left]);
                printf("odd_even philosopher %d fetch right chopstick %d failed and released left chopstick %d! \n",phi_id,right,left);
                return -1;
            }
            printf("odd_even philosopher %d fetched right chopstick %d !\n",phi_id,right);
            return 0;

        //奇数哲学家先取右边筷子，后取左边筷子
        }else{
            if(pthread_mutex_trylock(&mutex[right])!=0){
                return -1;
            }
            printf("odd_even philosopher %d fetched right chopstick %d !\n",phi_id,right);
            if(pthread_mutex_trylock(&mutex[left])!=0){
                pthread_mutex_unlock(&mutex[right]);
                printf("odd_even philosopher %d fetch right chopstick %d failed and released left chopstick %d! \n",phi_id,left,right);
                return -1;
            }
            printf("odd_even philosopher %d fetched left chopstick %d !\n",phi_id,left);
            return 0;
        }
    }
    return -1;
}

//哲学家放筷子
void Do_put_forks(int phi_id){
    pthread_mutex_unlock(&mutex[left]);
    printf("philosopher %d released  left chopstick %d !\n",phi_id,left);
	pthread_mutex_unlock(&mutex[right]);
    printf("philosopher %d released right chopstick %d !\n",phi_id,right);
}

//哲学家吃饭100秒并计数吃饭次数
void *philosopher(void *arg){
	int phi_id=*(int *)arg;
    time_t  start,end;
    start = time(NULL);
	while(1){
		Do_think(phi_id);
		while(Do_take_forks(phi_id)!=0){
            sleep(0.1);
            continue;
        }
		Do_eat(phi_id);
		Do_put_forks(phi_id);
        eat_times[phi_id]++;
        
        end = time(NULL);
        long duration =end - start;
        if(duration>100) break;
	}
	return NULL;
}


int main(int argc, char *argv[]){
	int num;

    //初始化互斥量
    for(num=0;num<phi_num;num++){
		pthread_mutex_init(&mutex[num],NULL);
        eat_times[num]=0;
	}

    //创建哲学家进程
	pthread_t *phi=(pthread_t*)malloc(sizeof(pthread_t)*phi_num);
	int *id=(int *)malloc(sizeof(int)*phi_num);
	for(num=0;num<phi_num;num++){
		id[num]=num;
		pthread_create(&phi[num],NULL,philosopher,(void*)(&id[num]));
	}

    //主线程阻塞，等待哲学家线程
	for(num=0;num<phi_num;num++){
		pthread_join(phi[num],NULL);
	}

    for(num=0;num<phi_num;num++){
        pthread_mutex_destroy(&mutex[num]);//销毁互斥量
        printf("philosopher %d have eated %d times !\n",num,eat_times[num]);
	}


	return 0;
}